use list::list::{ListEntry, ListValue, list_append, list_prepend, list_free, list_next, list_nth_entry, list_nth_data, list_length, list_remove_entry, list_remove_data, list_sort, list_find_data, list_to_array, list_iterate, list_iter_has_more, list_iter_next, list_iter_remove, list_prev, list_data, ListIterator};
use std::cmp::Ordering;
use std::ffi::c_void;

static mut VARIABLE1: i32 = 50;
static mut VARIABLE2: i32 = 0;
static mut VARIABLE3: i32 = 0;
static mut VARIABLE4: i32 = 0;

unsafe fn generate_list() -> *mut ListEntry {
    let mut list: *mut ListEntry = std::ptr::null_mut();
    assert!(!list_append(&mut list, &mut VARIABLE1 as *mut i32 as ListValue).is_null());
    assert!(!list_append(&mut list, &mut VARIABLE2 as *mut i32 as ListValue).is_null());
    assert!(!list_append(&mut list, &mut VARIABLE3 as *mut i32 as ListValue).is_null());
    assert!(!list_append(&mut list, &mut VARIABLE4 as *mut i32 as ListValue).is_null());
    list
}

unsafe fn check_list_integrity(list: *mut ListEntry) {
    let mut prev: *mut ListEntry = std::ptr::null_mut();
    let mut rover = list;

    while !rover.is_null() {
        assert_eq!(list_prev(rover), prev);
        prev = rover;
        rover = list_next(rover);
    }
}

#[test]
fn test_list_append() {
    unsafe {
        let mut list: *mut ListEntry = std::ptr::null_mut();
        assert!(!list_append(&mut list, &mut VARIABLE1 as *mut i32 as ListValue).is_null());
        check_list_integrity(list);
        assert!(!list_append(&mut list, &mut VARIABLE2 as *mut i32 as ListValue).is_null());
        check_list_integrity(list);
        assert!(!list_append(&mut list, &mut VARIABLE3 as *mut i32 as ListValue).is_null());
        check_list_integrity(list);
        assert!(!list_append(&mut list, &mut VARIABLE4 as *mut i32 as ListValue).is_null());
        check_list_integrity(list);

        assert_eq!(list_length(list), 4);

        assert_eq!(list_nth_data(list, 0), &mut VARIABLE1 as *mut i32 as ListValue);
        assert_eq!(list_nth_data(list, 1), &mut VARIABLE2 as *mut i32 as ListValue);
        assert_eq!(list_nth_data(list, 2), &mut VARIABLE3 as *mut i32 as ListValue);
        assert_eq!(list_nth_data(list, 3), &mut VARIABLE4 as *mut i32 as ListValue);

        list_free(list);
    }
}

#[test]
fn test_list_prepend() {
    unsafe {
        let mut list: *mut ListEntry = std::ptr::null_mut();
        assert!(!list_prepend(&mut list, &mut VARIABLE1 as *mut i32 as ListValue).is_null());
        check_list_integrity(list);
        assert!(!list_prepend(&mut list, &mut VARIABLE2 as *mut i32 as ListValue).is_null());
        check_list_integrity(list);
        assert!(!list_prepend(&mut list, &mut VARIABLE3 as *mut i32 as ListValue).is_null());
        check_list_integrity(list);
        assert!(!list_prepend(&mut list, &mut VARIABLE4 as *mut i32 as ListValue).is_null());
        check_list_integrity(list);

        assert_eq!(list_nth_data(list, 0), &mut VARIABLE4 as *mut i32 as ListValue);
        assert_eq!(list_nth_data(list, 1), &mut VARIABLE3 as *mut i32 as ListValue);
        assert_eq!(list_nth_data(list, 2), &mut VARIABLE2 as *mut i32 as ListValue);
        assert_eq!(list_nth_data(list, 3), &mut VARIABLE1 as *mut i32 as ListValue);

        list_free(list);
    }
}

#[test]
fn test_list_free() {
    unsafe {
        let list = generate_list();
        list_free(list);
        list_free(std::ptr::null_mut());
    }
}

#[test]
fn test_list_next() {
    unsafe {
        let list = generate_list();
        let mut rover = list;
        assert_eq!(list_data(rover), &mut VARIABLE1 as *mut i32 as ListValue);
        rover = list_next(rover);
        assert_eq!(list_data(rover), &mut VARIABLE2 as *mut i32 as ListValue);
        rover = list_next(rover);
        assert_eq!(list_data(rover), &mut VARIABLE3 as *mut i32 as ListValue);
        rover = list_next(rover);
        assert_eq!(list_data(rover), &mut VARIABLE4 as *mut i32 as ListValue);
        rover = list_next(rover);
        assert!(rover.is_null());
        list_free(list);
    }
}

#[test]
fn test_list_nth_entry() {
    unsafe {
        let list = generate_list();
        let mut entry = list_nth_entry(list, 0);
        assert_eq!(list_data(entry), &mut VARIABLE1 as *mut i32 as ListValue);
        entry = list_nth_entry(list, 1);
        assert_eq!(list_data(entry), &mut VARIABLE2 as *mut i32 as ListValue);
        entry = list_nth_entry(list, 2);
        assert_eq!(list_data(entry), &mut VARIABLE3 as *mut i32 as ListValue);
        entry = list_nth_entry(list, 3);
        assert_eq!(list_data(entry), &mut VARIABLE4 as *mut i32 as ListValue);
        entry = list_nth_entry(list, 4);
        assert!(entry.is_null());
        entry = list_nth_entry(list, 400);
        assert!(entry.is_null());
        list_free(list);
    }
}

#[test]
fn test_list_nth_data() {
    unsafe {
        let list = generate_list();
        assert_eq!(list_nth_data(list, 0), &mut VARIABLE1 as *mut i32 as ListValue);
        assert_eq!(list_nth_data(list, 1), &mut VARIABLE2 as *mut i32 as ListValue);
        assert_eq!(list_nth_data(list, 2), &mut VARIABLE3 as *mut i32 as ListValue);
        assert_eq!(list_nth_data(list, 3), &mut VARIABLE4 as *mut i32 as ListValue);
        assert!(list_nth_data(list, 4).is_null());
        assert!(list_nth_data(list, 400).is_null());
        list_free(list);
    }
}

#[test]
fn test_list_length() {
    unsafe {
        let mut list = generate_list();
        assert_eq!(list_length(list), 4);
        assert!(!list_prepend(&mut list, &mut VARIABLE1 as *mut i32 as ListValue).is_null());
        assert_eq!(list_length(list), 5);
        list_free(list);
        assert_eq!(list_length(std::ptr::null_mut()), 0);
    }
}

#[test]
fn test_list_remove_entry() {
    unsafe {
        let mut empty_list: *mut ListEntry = std::ptr::null_mut();
        let mut list = generate_list();
        let mut entry = list_nth_entry(list, 2);
        assert_eq!(list_remove_entry(&mut list, entry), 1);
        assert_eq!(list_length(list), 3);
        check_list_integrity(list);
        entry = list_nth_entry(list, 0);
        assert_eq!(list_remove_entry(&mut list, entry), 1);
        assert_eq!(list_length(list), 2);
        check_list_integrity(list);
        assert_eq!(list_remove_entry(&mut list, std::ptr::null_mut()), 0);
        assert_eq!(list_remove_entry(&mut empty_list, std::ptr::null_mut()), 0);
        list_free(list);
        list = std::ptr::null_mut();
        assert!(!list_append(&mut list, &mut VARIABLE1 as *mut i32 as ListValue).is_null());
        assert!(!list.is_null());
        let mut entry = list;
        assert_eq!(list_remove_entry(&mut list, entry), 1);
        assert!(list.is_null());
        list = generate_list();
        entry = list_nth_entry(list, 3);
        assert_eq!(list_remove_entry(&mut list, entry), 1);
        check_list_integrity(list);
        list_free(list);
    }
}

#[test]
fn test_list_remove_data() {
    unsafe {
        let entries = [89, 4, 23, 42, 4, 16, 15, 4, 8, 99, 50, 30, 4];
        let num_entries = entries.len();
        let mut list: *mut ListEntry = std::ptr::null_mut();
        for i in 0..num_entries {
            assert!(!list_prepend(&mut list, &entries[i] as *const i32 as ListValue).is_null());
        }
        let mut val = 0;
        assert_eq!(list_remove_data(&mut list, int_equal, &val as *const i32 as ListValue), 0);
        val = 56;
        assert_eq!(list_remove_data(&mut list, int_equal, &val as *const i32 as ListValue), 0);
        check_list_integrity(list);
        val = 8;
        assert_eq!(list_remove_data(&mut list, int_equal, &val as *const i32 as ListValue), 1);
        assert_eq!(list_length(list), num_entries as u32 - 1);
        check_list_integrity(list);
        val = 4;
        assert_eq!(list_remove_data(&mut list, int_equal, &val as *const i32 as ListValue), 4);
        assert_eq!(list_length(list), num_entries as u32 - 5);
        check_list_integrity(list);
        val = 89;
        assert_eq!(list_remove_data(&mut list, int_equal, &val as *const i32 as ListValue), 1);
        assert_eq!(list_length(list), num_entries as u32 - 6);
        check_list_integrity(list);
        list_free(list);
    }
}

#[test]
fn test_list_sort() {
    unsafe {
        let entries = [89, 4, 23, 42, 4, 16, 15, 4, 8, 99, 50, 30, 4];
        let sorted = [4, 4, 4, 4, 8, 15, 16, 23, 30, 42, 50, 89, 99];
        let num_entries = entries.len();
        let mut list: *mut ListEntry = std::ptr::null_mut();
        for i in 0..num_entries {
            assert!(!list_prepend(&mut list, &entries[i] as *const i32 as ListValue).is_null());
        }
        list_sort(&mut list, int_compare);
        assert_eq!(list_length(list), num_entries as u32);
        for i in 0..num_entries {
            let value = list_nth_data(list, i as u32) as *mut i32;
            assert_eq!(unsafe { *value }, sorted[i]);
        }
        list_free(list);
        list = std::ptr::null_mut();
        list_sort(&mut list, int_compare);
        assert!(list.is_null());
    }
}

#[test]
fn test_list_find_data() {
    unsafe {
        let entries = [89, 23, 42, 16, 15, 4, 8, 99, 50, 30];
        let num_entries = entries.len();
        let mut list: *mut ListEntry = std::ptr::null_mut();
        for i in 0..num_entries {
            assert!(!list_append(&mut list, &entries[i] as *const i32 as ListValue).is_null());
        }
        for i in 0..num_entries {
            let val = entries[i];
            let result = list_find_data(list, int_equal, &val as *const i32 as ListValue);
            assert!(!result.is_null());
            let data = list_data(result) as *mut i32;
            assert_eq!(unsafe { *data }, val);
        }
        let mut val = 0;
        assert!(list_find_data(list, int_equal, &val as *const i32 as ListValue).is_null());
        val = 56;
        assert!(list_find_data(list, int_equal, &val as *const i32 as ListValue).is_null());
        list_free(list);
    }
}

#[test]
fn test_list_to_array() {
    unsafe {
        let list = generate_list();
        let array = list_to_array(list);
        assert_eq!(unsafe { *array.offset(0) }, &mut VARIABLE1 as *mut i32 as ListValue);
        assert_eq!(unsafe { *array.offset(1) }, &mut VARIABLE2 as *mut i32 as ListValue);
        assert_eq!(unsafe { *array.offset(2) }, &mut VARIABLE3 as *mut i32 as ListValue);
        assert_eq!(unsafe { *array.offset(3) }, &mut VARIABLE4 as *mut i32 as ListValue);
        drop(unsafe { Box::from_raw(array) });
        list_free(list);
    }
}

#[test]
fn test_list_iterate() {
    unsafe {
        let mut list: *mut ListEntry = std::ptr::null_mut();
        let mut a = 0;
        for _ in 0..50 {
            assert!(!list_prepend(&mut list, &mut a as *mut i32 as ListValue).is_null());
        }
        let mut iter = ListIterator {
            prev_next: std::ptr::null_mut(),
            current: std::ptr::null_mut(),
        };
        list_iterate(&mut list, &mut iter);
        list_iter_remove(&mut iter);
        let mut counter = 0;
        while list_iter_has_more(&mut iter) {
            let data = list_iter_next(&mut iter) as *mut i32;
            counter += 1;
            if counter % 2 == 0 {
                list_iter_remove(&mut iter);
                list_iter_remove(&mut iter);
            }
        }
        assert!(list_iter_next(&mut iter).is_null());
        list_iter_remove(&mut iter);
        assert_eq!(counter, 50);
        assert_eq!(list_length(list), 25);
        list_free(list);
        list = std::ptr::null_mut();
        counter = 0;
        list_iterate(&mut list, &mut iter);
        while list_iter_has_more(&mut iter) {
            let _data = list_iter_next(&mut iter) as *mut i32;
            counter += 1;
        }
        assert_eq!(counter, 0);
    }
}

#[test]
fn test_list_iterate_bad_remove() {
    unsafe {
        let mut values = [0i32; 49];
        let mut list: *mut ListEntry = std::ptr::null_mut();
        for i in 0..49 {
            values[i] = i as i32;
            assert!(!list_prepend(&mut list, &values[i] as *const i32 as ListValue).is_null());
        }
        let mut iter = ListIterator {
            prev_next: std::ptr::null_mut(),
            current: std::ptr::null_mut(),
        };
        list_iterate(&mut list, &mut iter);
        while list_iter_has_more(&mut iter) {
            let val = list_iter_next(&mut iter) as *mut i32;
            if unsafe { *val } % 2 == 0 {
                assert_eq!(list_remove_data(&mut list, int_equal, val as ListValue), 1);
                list_iter_remove(&mut iter);
            }
        }
        list_free(list);
    }
}

unsafe fn int_equal(a: ListValue, b: ListValue) -> bool {
    *(a as *mut i32) == *(b as *mut i32)
}

unsafe fn int_compare(a: ListValue, b: ListValue) -> Ordering {
    let a_val = *(a as *mut i32);
    let b_val = *(b as *mut i32);
    if a_val < b_val {
        Ordering::Less
    } else if a_val > b_val {
        Ordering::Greater
    } else {
        Ordering::Equal
    }
}